/* global define CodeMirror */
/**
  * @ignore
  * BEGIN HEADER
  *
  * Contains:        Image rendering Plugin
  * CVM-Role:        CodeMirror Plugin
  * Maintainer:      Hendrik Erz
  * License:         GNU GPL v3
  *
  * Description:     This plugin renders images in-place.
  *
  * END HEADER
  */

(function (mod) {
  if (typeof exports === 'object' && typeof module === 'object') { // CommonJS
    mod(require('codemirror/lib/codemirror'))
  } else if (typeof define === 'function' && define.amd) { // AMD
    define(['codemirror/lib/codemirror'], mod)
  } else { // Plain browser env
    mod(CodeMirror)
  }
})(function (CodeMirror) {
  'use strict'

  // GENERAL PLUGIN VARIABLES
  const { getImageRE } = require('../../../regular-expressions')
  const makeAbsoluteURL = require('../../../util/make-absolute-url')
  const { trans } = require('../../../i18n-renderer')

  // Image detection regex
  const imageRE = getImageRE()

  // This variable holds a base64 encoded placeholder image.
  const img404 = ''

  /**
   * Defines the CodeMirror command to render all found markdown images.
   * @param  {CodeMirror} cm The calling CodeMirror instance
   * @return {void}    Commands do not return.
   */
  CodeMirror.commands.markdownRenderImages = function (cm) {
    // We'll only render the viewport
    const viewport = cm.getViewport()
    for (let i = viewport.from; i < viewport.to; i++) {
      if (cm.getModeAt({ 'line': i, 'ch': 0 }).name !== 'markdown-zkn') continue

      // Always reset lastIndex property, because test()-ing on regular
      // expressions advance it.
      imageRE.lastIndex = 0

      // First get the line and test if the contents contain an image
      let line = cm.getLine(i)
      if (!imageRE.test(line)) continue

      imageRE.lastIndex = 0 // Necessary because of global flag in RegExp

      let match

      // Run through all links on this line
      while ((match = imageRE.exec(line)) != null) {
        // The image RE will give us the following groups:
        // p1: The alternative text (in square brackets)
        // p2: The complete contents of the round braces
        // p3: If applicable, an image title (within round braces)
        // p4: Anything in curly brackets (mostly commands for Pandoc)
        let altText = match[1] || '' // Everything inside the square brackets
        let url = match[2] || '' // The URL
        let title = match[3] || altText // An optional title in quotes after the image
        let p4 = match[4] || ''

        // If a third group has been captured, we need to extract this from the
        // "bigger" second group again.
        if (match[3] !== undefined) {
          url = url.replace(`"${match[3]}"`, '').trim()
        }

        // Now get the precise beginning of the match and its end
        let curFrom = { 'line': i, 'ch': match.index }
        let curTo = { 'line': i, 'ch': match.index + match[0].length }

        let cur = cm.getCursor('from')
        if (cur.line === curFrom.line && cur.ch >= curFrom.ch && cur.ch <= curTo.ch + 1) {
          // Cursor is in selection: Do not render.
          continue
        }

        // We can only have one marker at any given position at any given time
        if (cm.doc.findMarks(curFrom, curTo).length > 0) continue

        // Do not render if it's inside a comment (in this case the mode will be
        // markdown, but comments shouldn't be included in rendering)
        // Final check to avoid it for as long as possible, as getTokenAt takes
        // considerable time.
        let tokenTypeBegin = cm.getTokenTypeAt(curFrom)
        let tokenTypeEnd = cm.getTokenTypeAt(curTo)
        if ((tokenTypeBegin && tokenTypeBegin.includes('comment')) ||
        (tokenTypeEnd && tokenTypeEnd.includes('comment'))) {
          continue
        }

        const img = new Image()

        const isDataUrl = /^data:[a-zA-Z0-9/;=]+(?:;base64){0,1},.+/.test(url)
        let actualURLToLoad = url

        if (!isDataUrl) {
          actualURLToLoad = makeAbsoluteURL(cm.getOption('zettlr').markdownImageBasePath, url)
        }

        const caption = document.createElement('figcaption')
        caption.textContent = title
        caption.contentEditable = true
        caption.onkeydown = function (event) {
          if (event.key === 'Enter') {
            event.preventDefault()
            event.stopPropagation()
            // Make sure there are no quotes since these will break the image
            const newCaption = caption.textContent.replace(/"/g, '')
            // "Why are you setting the caption both as the image description and title?"
            // Well, since all exports sometimes us this, sometimes the other value.
            const newImageTag = `![${newCaption}](${url} "${newCaption}")${p4}`
            // Now replace the underlying image
            cm.replaceRange(newImageTag, curFrom, curTo)
          }
        }

        const size = document.createElement('span')
        size.classList.add('image-size-info')

        const openExternally = document.createElement('span')
        openExternally.classList.add('open-externally-button')
        openExternally.textContent = 'Open image externally'
        openExternally.onclick = function (event) {
          // NOTE: We can only do this because the main process prevents any
          // navigation, and hence will capture this and instead open it using the shell.
          window.location.assign(makeAbsoluteURL(cm.getOption('zettlr').markdownImageBasePath, url))
        }

        const figure = document.createElement('figure')
        figure.appendChild(img)
        figure.appendChild(caption)
        figure.appendChild(size)
        if (!isDataUrl) {
          figure.appendChild(openExternally)
        }

        const container = document.createElement('div')
        container.classList.add('editor-image-container')
        container.appendChild(figure)

        // Now add a line widget to this line.
        let textMarker = cm.doc.markText(
          curFrom,
          curTo,
          {
            'clearOnEnter': true,
            'replacedWith': container,
            'handleMouseEvents': false
          }
        )

        // Retrieve the size constraints
        const maxPreviewWidth = Number(cm.getOption('zettlr').imagePreviewWidth)
        const maxPreviewHeight = Number(cm.getOption('zettlr').imagePreviewHeight)
        let width = (!Number.isNaN(maxPreviewWidth)) ? maxPreviewWidth + '%' : '100%'
        let height = (!Number.isNaN(maxPreviewHeight) && maxPreviewHeight < 100) ? maxPreviewHeight + 'vh' : ''

        // Apply the constraints to the figure and image
        figure.style.maxWidth = width
        figure.style.maxHeight = height
        img.style.maxWidth = width
        img.style.maxHeight = height
        img.alt = altText
        // Display a replacement image in case the correct one is not found
        img.onerror = () => {
          img.src = img404
          caption.textContent = trans('system.error.image_not_found', url)
        }
        img.onclick = () => { textMarker.clear() }

        // Update the image title on load to retrieve the real image size.
        img.onload = () => {
          img.title = `${title} (${img.naturalWidth}x${img.naturalHeight}px)`
          size.innerHTML = `${img.naturalWidth}&times;${img.naturalHeight}px`
          textMarker.changed()
        }

        // Finally set the src to begin the loading process of the image
        img.src = actualURLToLoad
      }
    }
  }
})
